#include "headers.h"
#include <math.h>

#define ACC 10.0 /* aceleracao em cm/s2 */
#define epsylon 1
#define DIST_COLISAO 0.0
#define DIST_CACAPA 1


void makeCollision(Ball *bMov, Ball *bStill) {
  bStill->Vx += bMov->Vx * 0.85;
  bStill->Vz += bMov->Vz * 0.85;
  bStill->emMovimento = 1;
  bMov->Vx *= 0.1;
  bMov->Vz *= 0.1;
}

double hipotenusa(double a, double b) {
  return sqrt(a * a + b * b);
}


double distancia (double posX1, double posZ1, double posX2, double posZ2) {
  return hipotenusa(posX1 - posX2,posZ1 - posZ2);
}

double distanciaCacapa(Ball b1, double posX, double posZ) {
  return distancia(b1.posX, b1.posZ, posX, posZ) - b1.raio - 7;
}

double distBalls(Ball b1, Ball b2) {
  double dist;
  dist = distancia(b1.posX, b1.posZ, b2.posX, b2.posZ) - b1.raio - b2.raio;
  //  printf("dist: %f", dist);
  return dist;
}

void ballCollision(Ball *b, Ball *balls){
  int i;
  double dist, dzAux, dxAux;
  for (i = 0; i < 8; i++){ 
    if ((b->id != balls[i].id) && (balls[i].existe))  {
      if((dist = distBalls(*b, balls[i])) < DIST_COLISAO) {
	double vt = hipotenusa(b->Vx, b->Vz);

	dxAux = -dist * b->Vx / vt;
	dzAux = -dist * b->Vz / vt;
	  
	balls[i].posZ += dzAux;
	balls[i].posX += dxAux;
	makeCollision(b, &balls[i]);
      }
    }
  }
}


void wallCollision(Ball *b)
{
  // verifica coord x na parede +x
  if ((b->posX + b->raio > LIM_MESA_X) && (b->Vx > 0.0)) {
    b->posX = LIM_MESA_X - b->raio - (b->posX - LIM_MESA_X);
    b->Vx *= -1;
  }
  if ((b->posX - b->raio < -LIM_MESA_X) && (b->Vx < 0.0)) {
    b->posX = -LIM_MESA_X + b->raio - (b->posX + LIM_MESA_X);
    b->Vx *= -1;
  }
  
  // verifica coord z na parede +z
  if ((b->posZ + b->raio > LIM_MESA_Z) && (b->Vz > 0.0)) {
    b->posZ = LIM_MESA_Z - b->raio - (b->posZ - LIM_MESA_Z);
    b->Vz *= -1;
  }
  if ((b->posZ - b->raio < -LIM_MESA_Z) && (b->Vz < 0.0)) {
    b->posZ = -LIM_MESA_Z + b->raio - (b->posZ + LIM_MESA_Z);
    b->Vz *= -1;

  }
}
void paraBola(Ball *b) {
   b->Vx = 0.0;
   b->Vz = 0.0;
   b->emMovimento = 0;
}

int paraBolaLenta(Ball *b) {
  if ((fabs(b->Vx) < epsylon) && (fabs(b->Vz) < epsylon)) {
    paraBola(b);
    return 1;
  }
  b->emMovimento = 1;
  return 0;
}

void mataBola(Ball *b) {
  paraBola(b);
  b->existe = 0;
}

void encacapa(Ball *b) {
  /*cacapas do meio*/
  if ((distanciaCacapa(*b, 0.0, 75.0) < DIST_CACAPA) && 
      (b->Vz > 0)) {
    mataBola(b);
    //printf("1");
  }

  if ((distanciaCacapa(*b, 0.0, -75.0) < DIST_CACAPA) && 
      (b->Vz < 0)) {
    mataBola(b);
    //printf("2");
  }
  /*cacapas +x*/
  if ((distanciaCacapa(*b, 144.0, 73.0) < DIST_CACAPA) && 
      (b->Vz > 0) && (b->Vx > 0)) {
    mataBola(b);
    //printf("3");
  }
  if ((distanciaCacapa(*b, 144.0, -73.0) < DIST_CACAPA) && 
      (b->Vz < 0) && (b->Vx > 0)) {
    mataBola(b);
    //printf("4");
  }
  /*cacapas -x*/
  if ((distanciaCacapa(*b, -144.0, 73.0) < DIST_CACAPA) && 
      (b->Vz > 0) && (b->Vx < 0)) {
    mataBola(b);
    //printf("5");
  }
  if ((distanciaCacapa(*b, -144.0, -73.0) < DIST_CACAPA) && 
      (b->Vz < 0) && (b->Vx < 0)) {
    mataBola(b);
    //    printf("6");
  }
}

/* compara o valor absolto dos valores*/
/* retorna o menor valor */
double menorAbsoluto(double v1, double v2) {
  if (fabs(v1) < fabs(v2))
    return v1;
  return v2;
}

void updateVelocities(Ball *b, float deltaT) {
  if (! paraBolaLenta(b)) {
    
  
  double accX, accZ;
  double vt;
  vt = hipotenusa(b->Vx, b->Vz);
  accX = ACC * (b->Vx / vt);
  accZ = ACC * (b->Vz / vt);
     
  double difVx, difVz;
  difVx = menorAbsoluto(accX * deltaT, b->Vx);
  difVz = menorAbsoluto(accZ * deltaT, b->Vz);
  
  b->Vx -= difVx;
  b->Vz -= difVz;

  paraBolaLenta(b);
  }
}

void updatePositions(Ball *b, float deltaT) {
  updateVelocities(b, deltaT);
  b->posX += b->Vx * deltaT;
  b->posZ += b->Vz * deltaT; 
}


/*
 * Ball *b a bola a ser atualizada
 * Ball **balls todas as bolas
 * float deltaT o tempo decorrido
 */
void updateBall(Ball *b, Ball *balls, float deltaT)
{
  /*   printf("deltat: %.2f,", deltaT); */
  /*   printf(" vx: %.2f, vz: %.2f, z: %f\n", b->Vx, b->Vz, b->posZ); */


  if (b->existe) {
    updatePositions(b, deltaT);
    encacapa(b);
    wallCollision(b);
    ballCollision(b, balls);

  }
}


